﻿using System;
using System.Collections.Generic;
using System.Core;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Linq2Crm
{
   public static class Extension
   {
      public static IQueryable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this IQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector, IEqualityComparer<TKey> comparer)
      {
         if (outer == null)
            throw new ArgumentNullException("outer");
         if (inner == null)
            throw new ArgumentNullException("inner");
         if (outerKeySelector == null)
            throw new ArgumentNullException("outerKeySelector");
         if (innerKeySelector == null)
            throw new ArgumentNullException("innerKeySelector");
         if (resultSelector == null)
            throw new ArgumentNullException("resultSelector");
         return outer.Provider.CreateQuery<TResult>((Expression)Expression.Call((Expression)null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TOuter), typeof(TInner), typeof(TKey), typeof(TResult)), outer.Expression, GetSourceExpression<TInner>(inner), (Expression)Expression.Quote((Expression)outerKeySelector), (Expression)Expression.Quote((Expression)innerKeySelector), (Expression)Expression.Quote((Expression)resultSelector), (Expression)Expression.Constant((object)comparer, typeof(IEqualityComparer<TKey>))));
      }

      public static IQueryable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(this IQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector)
      {
         if (outer == null)
            throw new ArgumentNullException("outer");
         if (inner == null)
            throw new ArgumentNullException("inner");
         if (outerKeySelector == null)
            throw new ArgumentNullException("outerKeySelector");
         if (innerKeySelector == null)
            throw new ArgumentNullException("innerKeySelector");
         if (resultSelector == null)
            throw new ArgumentNullException("resultSelector");
         return outer.Provider.CreateQuery<TResult>((Expression)Expression.Call((Expression)null, ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TOuter), typeof(TInner), typeof(TKey), typeof(TResult)), outer.Expression, GetSourceExpression<TInner>(inner), (Expression)Expression.Quote((Expression)outerKeySelector), (Expression)Expression.Quote((Expression)innerKeySelector), (Expression)Expression.Quote((Expression)resultSelector)));
      }

      private static Expression GetSourceExpression<TSource>(IEnumerable<TSource> source)
      {
         IQueryable<TSource> queryable = source as IQueryable<TSource>;
         if (queryable != null)
            return queryable.Expression;
         else
            return (Expression)Expression.Constant((object)source, typeof(IEnumerable<TSource>));
      }
   }
}